// Package models 定义比较相关的数据模型
package models

import (
	"bytes" // 导入 bytes 包用于比较字节切片
	"fmt"
	"px-table-checksum-go/util" // 导入 util 包以使用 FormatForCRC 和 KeyString 常量
	"sort"                      // 导入 sort 包用于排序
	"strings"

	"github.com/sirupsen/logrus" // 导入 logrus 包
)

// ComparableData 表示可比较的数据接口
type ComparableData interface {
	// GetKey 获取数据的键值标识
	GetKey() string

	// GetData 获取数据内容
	GetData() map[string]interface{}

	// Equals 比较两个数据是否相等
	Equals(other ComparableData) bool

	// FindDifferences 找出两个数据之间的差异列
	// 返回一个map，键为列名，值为包含源值和目标值的差异对象
	FindDifferences(other ComparableData) map[string]ColumnDiff
}

// TableRowData 表示表中的一行数据
type TableRowData struct {
	// 数据所属表信息
	SchemaName string
	TableName  string

	// 行数据
	// key: 列名, value: 列值
	Data map[string]interface{}

	// 键信息
	KeyColumns []string
}

// GetKey 获取行的键值标识 (键列组合成的字符串)
// Modify: Use util.KEY_PART_SEPARATOR and util.NULL_KEY_PART
func (r *TableRowData) GetKey() string {
	if r == nil || len(r.KeyColumns) == 0 || r.Data == nil {
		return ""
	}

	var keyParts []string
	for _, col := range r.KeyColumns {
		if val, ok := r.Data[col]; ok {
			if val == nil {
				keyParts = append(keyParts, util.NULL_KEY_PART) // Use NULL_KEY_PART
			} else {
				// Use fmt.Sprintf for key string representation.
				// Note: While util.FormatKeyToString exists, GetKey is for comparison sorting.
				// Simple string format might be sufficient here, but relies on consistent
				// fmt.Sprintf behavior across types and Go versions.
				// A robust solution might need type-aware key part formatting.
				// For now, stick to fmt.Sprintf as it's standard Go behavior.
				keyParts = append(keyParts, fmt.Sprintf("%v", val))
			}
		} else {
			// This case indicates an issue where KeyColumns list doesn't match actual row data.
			// Should log a warning or error if this happens in practice.
			logrus.Warnf("TableRowData.GetKey: 行数据中缺少键列 '%s' 的值。行数据: %+v", col, r.Data)
			keyParts = append(keyParts, "MISSING_KEY_COLUMN") // Placeholder for missing key part
		}
	}

	// Use util.KEY_PART_SEPARATOR
	return strings.Join(keyParts, util.KEY_PART_SEPARATOR)
}

// GetData 获取行数据
func (r *TableRowData) GetData() map[string]interface{} {
	if r == nil {
		return nil
	}
	return r.Data
}

// Equals 比较两行数据是否完全相等
// Modify: Use util.FormatForCRC for consistent comparison
func (r *TableRowData) Equals(other ComparableData) bool {
	if r == nil && other == nil {
		return true
	}
	if r == nil || other == nil {
		return false
	}

	otherRow, ok := other.(*TableRowData)
	if !ok {
		return false // Type mismatch
	}

	// Compare column count. Note: This assumes both rows have the same columns.
	// If schemas differ, SELECT * might return different columns, leading to false negatives or positives.
	// Robust schema comparison/handling is outside the scope of this function.
	if len(r.Data) != len(otherRow.Data) {
		return false
	}

	// Compare each column's value using formatted bytes
	// Iterate using keys from one map (e.g., r.Data), assuming both maps have the same keys if lengths match.
	for col, val := range r.Data {
		otherVal, exists := otherRow.Data[col]
		if !exists {
			// Column exists in r but not in otherRow.Data. Should not happen if column counts matched.
			// Log error and return false.
			logrus.Errorf("TableRowData.Equals: 列 '%s' 在源行中存在但在目标行中缺失", col)
			return false
		}

		// Use util.FormatForCRC for consistent byte representation
		bytes1 := util.FormatForCRC(val)
		bytes2 := util.FormatForCRC(otherVal) // Corrected from otherVal to otherRow.Data[col] was also wrong, this is correct now

		// Compare the byte slices
		if !bytes.Equal(bytes1, bytes2) {
			return false
		}
	}

	return true
}

// FindDifferences 找出两行数据之间的差异列
// Modify: Use util.FormatForCRC for consistent comparison
func (r *TableRowData) FindDifferences(other ComparableData) map[string]ColumnDiff {
	if r == nil || other == nil {
		return nil // Cannot compare if either is nil
	}

	otherRow, ok := other.(*TableRowData)
	if !ok {
		return nil // Type mismatch
	}

	// Create a set of all column names present in either row
	allColumns := make(map[string]struct{})
	for col := range r.Data {
		allColumns[col] = struct{}{}
	}
	for col := range otherRow.Data {
		allColumns[col] = struct{}{}
	}

	// Create the differences map
	differences := make(map[string]ColumnDiff)

	// Iterate over all columns, find differences
	for col := range allColumns {
		val1, has1 := r.Data[col]        // Value in source row
		val2, has2 := otherRow.Data[col] // Value in target row

		// Handle cases where a column exists in one but not the other
		// This indicates a schema difference.
		if !has1 || !has2 {
			differences[col] = ColumnDiff{
				SourceValue: val1, // Will be nil if !has1
				TargetValue: val2, // Will be nil if !has2
			}
			continue // Move to next column
		}

		// Use util.FormatForCRC for consistent byte representation comparison
		bytes1 := util.FormatForCRC(val1)
		bytes2 := util.FormatForCRC(val2) // Corrected from otherVal

		// Compare the byte slices
		if !bytes.Equal(bytes1, bytes2) {
			differences[col] = ColumnDiff{
				SourceValue: val1, // Original Go value
				TargetValue: val2, // Original Go value
			}
		}
	}

	return differences
}

// NewTableRowData 创建一个新的表行数据对象
func NewTableRowData(schemaName, tableName string, data map[string]interface{}, keyColumns []string) *TableRowData {
	// Store a copy of data to prevent external modifications affecting this object.
	dataCopy := make(map[string]interface{}, len(data))
	for k, v := range data {
		dataCopy[k] = v
	}

	// Store a copy of keyColumns slice.
	keyColumnsCopy := make([]string, len(keyColumns))
	copy(keyColumnsCopy, keyColumns)

	return &TableRowData{
		SchemaName: schemaName,
		TableName:  tableName,
		Data:       dataCopy,       // Use the copy
		KeyColumns: keyColumnsCopy, // Use the copy
	}
}

// CompareResult 表示表数据比较的结果
type CompareResult struct {
	// 基本信息
	SchemaName string
	TableName  string

	// 差异统计
	TotalRowsSource int // 源表总行数
	TotalRowsTarget int // 目标表总行数
	MatchedRows     int // 完全匹配的行数
	OnlyInSource    int // 仅在源表中存在的行数
	OnlyInTarget    int // 仅在目标表中存在的行数
	MismatchedRows  int // 键相同但内容不同的行数

	// 详细差异 (根据需要可能只记录部分数据)
	SourceOnlyRows []*TableRowData  // 仅在源表中的行
	TargetOnlyRows []*TableRowData  // 仅在目标表中的行
	DifferentRows  []*RowDifference // 内容不同的行
}

// RowDifference 表示一行的差异
type RowDifference struct {
	// 键标识 (string format for logging/debug)
	Key string

	// Source and target row data (pointers to original TableRowData)
	// These pointers should not be nil for entries in DifferentRows list.
	SourceRow *TableRowData
	TargetRow *TableRowData

	// Differing columns
	DifferentColumns map[string]ColumnDiff
}

// CompareRows 比较两组行数据，生成比较结果
// Modify: Sort rows by key before comparison for correctness and efficiency
func CompareRows(schemaName, tableName string, sourceRows, targetRows []*TableRowData) *CompareResult {
	result := &CompareResult{
		SchemaName: schemaName,
		TableName:  tableName,

		TotalRowsSource: len(sourceRows),
		TotalRowsTarget: len(targetRows),

		SourceOnlyRows: make([]*TableRowData, 0),
		TargetOnlyRows: make([]*TableRowData, 0),
		DifferentRows:  make([]*RowDifference, 0), // For rows with matching keys but differing content
	}

	// Sort rows by key for efficient linear scan comparison
	sortRowsByKey(sourceRows)
	sortRowsByKey(targetRows)

	// Use two pointers for linear scan comparison after sorting
	i, j := 0, 0 // Pointers for sourceRows and targetRows
	for i < len(sourceRows) || j < len(targetRows) {
		var sourceRow, targetRow *TableRowData
		var sourceKey, targetKey string

		// Get current row and key from source (if available)
		if i < len(sourceRows) {
			sourceRow = sourceRows[i]
			sourceKey = sourceRow.GetKey()
		}

		// Get current row and key from target (if available)
		if j < len(targetRows) {
			targetRow = targetRows[j]
			targetKey = targetRow.GetKey()
		}

		// Compare keys to find matches, source-only, or target-only rows
		// Use string comparison of keys. This assumes GetKey() produces comparable strings.

		// Case 1: Source row exists, but target row is nil or has a greater key
		if sourceRow != nil && (targetRow == nil || sourceKey < targetKey) {
			// This is a source-only row
			result.SourceOnlyRows = append(result.SourceOnlyRows, sourceRow) // Add the source row to the list
			result.OnlyInSource++
			i++ // Move to the next source row

			// Case 2: Target row exists, but source row is nil or has a greater key
		} else if targetRow != nil && (sourceRow == nil || targetKey < sourceKey) {
			// This is a target-only row
			result.TargetOnlyRows = append(result.TargetOnlyRows, targetRow) // Add the target row to the list
			result.OnlyInTarget++
			j++ // Move to the next target row

			// Case 3: Both source and target rows exist and have the same key
		} else if sourceRow != nil && targetRow != nil && sourceKey == targetKey {
			// Keys match, compare content
			if sourceRow.Equals(targetRow) {
				// Content matches
				result.MatchedRows++
			} else {
				// Content differs, find specific column differences
				diffColumns := sourceRow.FindDifferences(targetRow)
				// Add to DifferentRows list only if there are actual column differences found
				if len(diffColumns) > 0 {
					result.DifferentRows = append(result.DifferentRows, &RowDifference{
						Key:              sourceKey, // Use the matching key
						SourceRow:        sourceRow, // Store pointers to original rows
						TargetRow:        targetRow,
						DifferentColumns: diffColumns, // Store the discovered column diffs
					})
					result.MismatchedRows++
				} else {
					// This case indicates CRC might have differed, but row comparison found no column differences.
					// Log a warning as this is unexpected.
					// Treat as a match for counts purposes? Or as an unfixable discrepancy?
					// Let's log and count as mismatched, but note no specific column diffs found for fixing.
					// The decision to count as mismatched is based on the CRC mismatch which led to this comparison.
					// The lack of column diffs means we can't generate a repair SQL.
					// Let's count as mismatched, but the generated SQL list will be empty for this row.
					logrus.Warnf("CompareRows: Keys match (%s), but content differs (CRC mismatch?) yet no column differences found by FindDifferences. Schema: %s, Table: %s.",
						sourceKey, schemaName, tableName)
					// We still add a RowDifference entry but with empty DiffColumns.
					// This allows generateFixSQLs to see this row needs attention but skip generating UPDATE.
					result.DifferentRows = append(result.DifferentRows, &RowDifference{
						Key:              sourceKey,
						SourceRow:        sourceRow,
						TargetRow:        targetRow,
						DifferentColumns: diffColumns, // Empty map
					})
					result.MismatchedRows++
				}
			}
			i++ // Move to the next row in both slices
			j++
		} else {
			// This 'else' case should theoretically not be reached with the conditions above,
			// but as a defensive measure, log if it does.
			logrus.Errorf("CompareRows: Unexpected state in comparison loop. i=%d, j=%d, len(sourceRows)=%d, len(targetRows)=%d",
				i, j, len(sourceRows), len(targetRows))
			// To prevent infinite loop in unexpected state, increment pointers or break.
			// Let's increment both to try and move past the issue.
			i++
			j++
		}
	}

	return result
}

// sortRowsByKey sorts a slice of TableRowData by their composite key string representation.
// This is critical for the two-pointer comparison in CompareRows.
func sortRowsByKey(rows []*TableRowData) {
	// Use standard library sort.Slice for efficiency.
	// It requires a comparison function (less function).
	sort.Slice(rows, func(i, j int) bool {
		// Compare the string representation of the keys.
		// This assumes that GetKey() produces strings that can be correctly ordered
		// lexicographically to match the database's key ordering.
		// For simple types and well-defined composite keys, this is often true.
		// For complex types (like spatial) or unusual character sets/collations,
		// a more sophisticated comparison might be needed, potentially involving
		// database-specific collation rules or comparing parsed key components individually.
		return rows[i].GetKey() < rows[j].GetKey()
	})
}

// HasDifferences checks if the CompareResult indicates any differences.
func (r *CompareResult) HasDifferences() bool {
	if r == nil {
		return false
	}
	return r.OnlyInSource > 0 || r.OnlyInTarget > 0 || r.MismatchedRows > 0
}

// GetSummary 获取比较结果的摘要
func (r *CompareResult) GetSummary() string {
	if r == nil {
		return "比较结果为空"
	}

	return fmt.Sprintf(
		"表 %s.%s 比较结果: 源表 %d 行, 目标表 %d 行, 匹配 %d 行, 源表独有 %d 行, 目标表独有 %d 行, 不匹配 %d 行",
		r.SchemaName, r.TableName,
		r.TotalRowsSource, r.TotalRowsTarget,
		r.MatchedRows, r.OnlyInSource, r.OnlyInTarget, r.MismatchedRows,
	)
}

// convertToMapSlice 辅助函数 - 将TableRowData转换为map切片
// Used for SourceOnlyRows and TargetOnlyRows lists in DiffResult (for compatibility).
func convertToMapSlice(rows []*TableRowData) []map[string]interface{} {
	logger := logrus.WithField("func", "convertToMapSlice")

	if rows == nil {
		logger.Debug("输入行数据数组为 nil") // Change to Debug
		return nil
	}

	logger.Debugf("开始转换 %d 条行数据", len(rows))
	if len(rows) == 0 {
		logger.Debug("没有行数据需要转换")
		return []map[string]interface{}{} // Return empty slice, not nil, if input is empty but not nil
	}

	result := make([]map[string]interface{}, 0, len(rows)) // Use 0 initial length, append manually
	for i, row := range rows {
		if row == nil {
			logger.Warnf("第 %d 行数据为 nil，跳过", i+1)
			continue
		}

		if row.Data == nil {
			logger.Warnf("第 %d 行的数据字段为 nil，跳过", i+1)
			continue
		}

		// row.Data should be a map. Since NewTableRowData copies the data,
		// we can use it directly or create another copy if absolutely necessary,
		// but it's already a copy of the scanned map.
		// Let's append the map directly as DiffResult wants map[string]interface{}.
		result = append(result, row.Data) // Append the data map

		if i < 3 {
			logger.Debugf("第 %d 行数据转换成功: 列数=%d (示例)", i+1, len(row.Data))
		}
	}

	logger.Debugf("转换完成: 输入 %d 条, 输出 %d 条", len(rows), len(result))
	return result
}

// convertToDiffDetails 辅助函数 - 将RowDifference转换为RowDiff
// Used for DifferentDetails list in DiffResult (for compatibility).
func convertToDiffDetails(rows []*RowDifference) []RowDiff {
	logger := logrus.WithField("func", "convertToDiffDetails")

	if rows == nil {
		logger.Debug("输入行差异数组为 nil") // Change to Debug
		return nil
	}

	logger.Debugf("开始转换 %d 条行差异数据", len(rows))
	if len(rows) == 0 {
		logger.Debug("没有行差异数据需要转换")
		return []RowDiff{} // Return empty slice, not nil, if input is empty but not nil
	}

	result := make([]RowDiff, 0, len(rows)) // Use 0 initial length, append manually
	for i, row := range rows {
		if row == nil {
			logger.Warnf("第 %d 条行差异数据为 nil，跳过", i+1)
			continue
		}

		// According to CompareRows logic, RowDifference entries should have
		// non-nil SourceRow, TargetRow, and non-empty DifferentColumns.
		// Add defensive checks anyway.
		if row.SourceRow == nil || row.TargetRow == nil || len(row.DifferentColumns) == 0 {
			logger.Warnf("第 %d 条行差异数据不完整 (SourceRow=%t, TargetRow=%t, DiffColumns.len=%d)，跳过转换",
				i+1, row.SourceRow != nil, row.TargetRow != nil, len(row.DifferentColumns))
			continue
		}

		// Extract key map from SourceRow.Data using KeyColumns list
		keyMap := make(map[string]interface{}, len(row.SourceRow.KeyColumns))
		// Use row.SourceRow.KeyColumns for ordered and correct key column names
		for _, col := range row.SourceRow.KeyColumns {
			if val, ok := row.SourceRow.Data[col]; ok {
				keyMap[col] = val
			} else {
				// This should not happen if KeyColumns match Data keys, but defensive check.
				logger.Errorf("第 %d 条行差异的键列 '%s' 在源行数据中不存在，跳过此差异", i+1, col)
				// Cannot form a valid key for this RowDiff, skip.
				continue
			}
		}

		// Check if all key columns were successfully extracted
		if len(keyMap) != len(row.SourceRow.KeyColumns) {
			logger.Errorf("第 %d 条行差异键列提取不完整 (%d/%d)，跳过此差异", i+1, len(keyMap), len(row.SourceRow.KeyColumns))
			continue
		}

		// Copy SourceData and TargetData maps for the RowDiff structure
		sourceDataCopy := make(map[string]interface{}, len(row.SourceRow.Data))
		for k, v := range row.SourceRow.Data {
			sourceDataCopy[k] = v
		}
		targetDataCopy := make(map[string]interface{}, len(row.TargetRow.Data))
		for k, v := range row.TargetRow.Data {
			targetDataCopy[k] = v
		}

		// Copy DiffColumns map
		diffColumnsCopy := make(map[string]ColumnDiff, len(row.DifferentColumns))
		for k, v := range row.DifferentColumns {
			diffColumnsCopy[k] = v // ColumnDiff struct is copyable
		}

		rowDiff := RowDiff{
			Key:         keyMap,          // Use the extracted key map
			SourceData:  sourceDataCopy,  // Use the copy
			TargetData:  targetDataCopy,  // Use the copy
			DiffColumns: diffColumnsCopy, // Use the copy
		}

		result = append(result, rowDiff)

		if i < 3 || logrus.GetLevel() >= logrus.DebugLevel {
			logger.Debugf("第 %d 条行差异转换结果: 键='%s', 差异列数=%d",
				i+1, row.Key, len(diffColumnsCopy)) // Log the string key for easy identification
			// Optionally log sample diff columns in debug
			// for colName, colDiff := range diffColumnsCopy {
			//     logrus.Debugf("  - Diff Col: %s, Source: %v, Target: %v", colName, colDiff.SourceValue, colDiff.TargetValue)
			// }
		}
	}

	logger.Debugf("转换完成: 输入 %d 条, 输出 %d 条", len(rows), len(result))
	return result
}
